<!-- 可拖拽的模态框 -->
<template>
  <a-modal v-bind="newProps" @cancel="emits('cancel', false)" :wrapClassName="wrapClassName">
    <!-- title 区域用于点击拖拽 -->
    <template #title>
      <div ref="modalTitleRef" class="title">{{ title }}</div>
    </template>

    <!-- 内容区域 -->
    <slot></slot>

    <!-- 自定义渲染 实现拖拽效果 -->
    <template #modalRender="{ originVNode }">
      <div :style="transformStyle">
        <component :is="originVNode" />
      </div>
    </template>
  </a-modal>
</template>
<script setup lang="ts">
  import { ref, computed, watch, watchEffect, type CSSProperties, useTemplateRef } from 'vue';
  import { useDraggable } from '@vueuse/core';
  import type { ModalProps } from 'ant-design-vue';
  import { omit } from 'lodash-es';

  interface DraggableModalProps
    extends Pick<ModalProps, 'title' | 'open' | 'width' | 'footer' | 'wrapClassName' | 'maskClosable'> {}

  const props = withDefaults(defineProps<DraggableModalProps>(), {});
  const newProps = computed(() => {
    return omit(props, 'title', 'wrapClassName');
  });
  const wrapClassName = computed(() => {
    return ['draggable-modal', props.wrapClassName].join(' ');
  });

  const emits = defineEmits<{
    cancel: [val: boolean];
  }>();
  /* ----------------------- 拖拽逻辑代码 ----------------------- */
  const modalTitleRef = useTemplateRef('modalTitleRef');
  const { x, y, isDragging } = useDraggable(modalTitleRef);

  // 拖拽起始位置
  const startX = ref(0);
  const startY = ref(0);
  const startedDrag = ref(false);

  // 当前变换的坐标
  const transformX = ref(0);
  const transformY = ref(0);

  // 拖拽前的变换坐标
  const preTransformX = ref(0);
  const preTransformY = ref(0);

  // 拖拽区域的边界矩形
  const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 });

  // 监听x、y坐标变化，更新拖拽起始位置和边界信息
  watch([x, y], () => {
    if (!startedDrag.value && modalTitleRef.value) {
      startX.value = x.value;
      startY.value = y.value;

      const bodyRect = document.body.getBoundingClientRect();
      const titleRect = modalTitleRef.value.getBoundingClientRect();
      dragRect.value.right = bodyRect.width - titleRect.width;
      dragRect.value.bottom = bodyRect.height - titleRect.height;

      preTransformX.value = transformX.value;
      preTransformY.value = transformY.value;
    }
    startedDrag.value = true;
  });
  watch(isDragging, () => {
    if (!isDragging) {
      startedDrag.value = false;
    }
  });

  // 计算拖拽过程中的变换坐标，确保在有效范围内
  watchEffect(() => {
    if (startedDrag.value) {
      transformX.value =
        preTransformX.value + Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) - startX.value;
      transformY.value =
        preTransformY.value + Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) - startY.value;
    }
  });

  // 通过计算样式移动弹窗
  const transformStyle = computed<CSSProperties>(() => {
    return {
      transform: `translate(${transformX.value}px, ${transformY.value}px)`,
    };
  });
</script>
<style lang="less" scoped>
  :deep(.ant-modal-content) {
    padding-top: 0;
  }
  .draggable-modal {
    .title {
      padding-top: 20px;
      width: 100%;
      cursor: move;
    }
  }
</style>
